#ifndef __M_EXCHANGE_H__
#define __M_EXCHANGE_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <google/protobuf/map.h>

namespace zymq
{
    // 1. 交换机类
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;
        // 1. 交换机名称
        std::string name;
        // 2. 交换机类型
        ExchangeType type;
        // 3. 交换机持久化标志
        bool durable;
        // 4. 是否自动删除标志
        bool auto_delete;
        // 5. 其他参数
        google::protobuf::Map<std::string, std::string> args;

        Exchange() {}
        Exchange(const std::string &ename,
                 ExchangeType etype,
                 bool edurable,
                 bool eauto_delete,
                 const google::protobuf::Map<std::string, std::string> &eargs)
            : name(ename), type(etype), durable(edurable),
              auto_delete(eauto_delete), args(eargs) {}

        // args存储键值对，在存储数据库的时候，会组织一个格式字符串进行存储 key=val&key=val....
        // 内部解析str_args字符串，将内容存储到成员中
        void setArgs(const std::string &str_args)
        {
            std::vector<std::string> sub_args;
            StrHelper::split(str_args, "&", sub_args);
            for (auto &str : sub_args)
            {
                std::vector<std::string> kv;
                StrHelper::split(str, "=", kv);
                args[kv[0]] = kv[1];
            }
        }

        std::string getArgs()
        {
            std::string ret;
            for (auto &kv : args)
            {
                ret += kv.first;
                ret += "=";
                ret += kv.second;
                ret += "&";
            }
            return ret;
        }
    };

    // 通过交换机名称，直接找到该交换机所有的属性成员
    using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;

    // 2. 交换机数据持久化管理类,数据存储到sqlite数据库中
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string ppath = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(ppath);
            assert(_sql_helper.open());

            createTable();
        }
        void createTable()
        {
            #define CREATE_EXCHANGE_TABLE "create table if not exists exchange_table(\
                name varchar(32) primary key,\
                type int,\
                durable int,\
                auto_delete int,\
                args varchar(128));"

            if(_sql_helper.exec(CREATE_EXCHANGE_TABLE, nullptr, nullptr) == false)
            {
                ELOG("创建交换机数据库表失败!!");
                abort();
            }
        }
        void removeTable()
        {
            #define DROP_EXCHANGE_TABLE "drop table if exists exchange_table;"

            if(_sql_helper.exec(DROP_EXCHANGE_TABLE, nullptr, nullptr) == false)
            {
                ELOG("删除交换机数据库表失败!!");
            }
        }
        bool insert(Exchange::ptr &ex)
        {
            #define INSERT_EXCHANGE_SQL "insert into exchange_table values('%s', %d, %d, %d, '%s');"
            
            char sql_str[4096] = {0};
            std::string args_str = ex->getArgs();
            sprintf(sql_str, INSERT_EXCHANGE_SQL, ex->name.c_str(), ex->type, ex->durable, ex->auto_delete, args_str.c_str());

            if(_sql_helper.exec(sql_str, nullptr, nullptr) == false)
            {
                ELOG("新增数据失败!!");
                return false;
            }
            return true;
        }
        void remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name=";
            ss << "'" << name << "';";

            if(_sql_helper.exec(ss.str(), nullptr, nullptr) == false)
            {
                ELOG("删除数据失败!!");
                return;
            }
        }


        // 查询exchange_table表中的所有数据，返回给一个map中
        ExchangeMap recovery()
        {
            ExchangeMap ret;
            #define SELECT_EXCHANGE_SQL "select name, type, durable, auto_delete, args from exchange_table;"

            _sql_helper.exec(SELECT_EXCHANGE_SQL, selectCallback,&ret);

            return ret;
        }
    private:
        static int selectCallback(void *arg, int col_count, char **result, char **fields_name)
        {
            ExchangeMap* pret = (ExchangeMap*)arg;

            Exchange::ptr exp = std::make_shared<Exchange>();
            exp->name = result[0];
            exp->type = (zymq::ExchangeType)std::stoi(result[1]);
            exp->durable = std::stoi(result[2]);
            exp->auto_delete = std::stoi(result[3]);
            if(result[4]) exp->setArgs(result[4]);

            pret->insert({result[0], exp});

            return 0;
        }
    private:
        SqliteHelper _sql_helper;
    };

    // 3. 交换机数据内存管理类
    class ExchangeManager
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;

        ExchangeManager(const std::string &dbfile):_mapper(dbfile)
        {
            // 从数据库中恢复数据，就是重新加载到程序（内存中） 方便操作
            _exchanges = _mapper.recovery();
        }
        bool declareExchange(const std::string &name,
                             ExchangeType type, bool durable, bool auto_delete,
                             const google::protobuf::Map<std::string, std::string> &args)
        {
            // 先锁起来，再查询
            std::unique_lock<std::mutex> lock(_mutex);
            if(_exchanges.find(name) == _exchanges.end())
            {
                Exchange::ptr ep = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
                if(durable == true)
                {
                    bool ret = _mapper.insert(ep);
                    if(ret == false) return false;
                }
                _exchanges.insert(std::make_pair(name, ep));
                return true;
            }
            return true;
        }
        void deleteExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            // 交换机不存在，直接退出
            if(it == _exchanges.end()) return;

            // 是持久存储，才从数据库中删除
            if(it->second->durable == true) _mapper.remove(name);
            _exchanges.erase(name);
        }
        Exchange::ptr selectExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            // 交换机不存在，直接退出
            if(it == _exchanges.end()) return nullptr;

            return it->second;
        }
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            // 交换机不存在，直接退出
            if(it == _exchanges.end()) return false;

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _exchanges.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }

    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        ExchangeMap _exchanges;
    };
}

#endif